package com.zjp;

import cn.hutool.core.util.RandomUtil;
import cn.hutool.json.JSONUtil;
import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import com.google.common.cache.*;
import com.google.common.collect.*;
import org.junit.jupiter.api.Test;

import java.time.Duration;
import java.util.*;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static java.util.stream.Collectors.toList;

public class BaseMainTest {

    public static void main(String[] args) {
        System.out.println("main");
    }

    static List<String> stringList = Lists.newArrayList("a", "b", "c", "d", "", null, "", "");

    @Test
    public void joinerListTest() {
        // 出现null使用value值替换
        String join = Joiner.on(",").useForNull("V").join(stringList);
        System.out.println(join);
        // 出现null直接跳过
        String result = Joiner.on(",").skipNulls().join(stringList);
        System.out.println(result);
    }

    @Test
    public void withMapTest() {
        // map 连接器
        Map<Integer, String> maps = Maps.newHashMap();
        maps.put(1, "呵呵");
        maps.put(2, "嘿嘿");

        String result = Joiner.on("-").withKeyValueSeparator(":").join(maps);
        System.out.println(result);
        System.out.println(maps);
        //1:呵呵-2:嘿嘿
        //{1=呵呵, 2=嘿嘿}
    }

    @Test
    public void splitterListTest() {
        String str = "今，天，不，加，班";
        // 简单分割
        List<String> list = Splitter.on(",").splitToList(str);
        System.out.println(list);
        // [今，天，不，加，班]

        String str2 = "1 2,5,4 , , 09,u";
        // 去除前后空格
        List<String> list2 = Splitter.on(",").trimResults().splitToList(str2);
        System.out.println(list2);
        String str3 = "1 2,5,4 ,, 09,u";
        // 去除分割后空的字符串
        List<String> list3 = Splitter.on(",").omitEmptyStrings().splitToList(str3);
        System.out.println(list3);

        String test = "3344778899";
        // 按固定长度分割
        List<String> list4 = Splitter.fixedLength(2).splitToList(test);
        System.out.println(list4);
        // [33, 44, 77, 88, 99]
    }


    @Test
    public void multisetTest() {

        String[] words = {"好", "不错", "不错"};
        Multiset<String> multiset = HashMultiset.create();
        multiset.addAll(Arrays.asList(words));
        int count = multiset.count("不错");
        System.out.println(count);
    }


    @Test
    public void biMapTest() {
        BiMap<String, String> biMap = HashBiMap.create();


        biMap.put("123", "456");
        // 通过key找Value
        System.out.println(biMap.get("123"));
        // 通过value找Key
        System.out.println(biMap.inverse().get("456"));

        // 对于已经存在的value 不能直接put替换
        // biMap.put("678", "456");
        // 要强制替换key
        biMap.forcePut("678", "456");
        System.out.println(biMap.get("678"));
    }

    public static void listCreateTest() {
        // 将数组转化成list,并在开头位置插入元素
        List<String> list = Lists.asList("a", new String[]{"b", "c"});
        List<String> list2 = Lists.asList("a", "b", new String[]{"c", "d", "e"});

        System.out.println(list);
        System.out.println(list2);


        // 直接创建ArrayList
        ArrayList<String> arrayList = Lists.newArrayList();

        // 创建ArrayList 并初始化
        ArrayList<String> list3 = Lists.newArrayList("a", "b", "c");
        // 基于现有的arrayList,创建一个arrayList
        ArrayList<String> list4 = Lists.newArrayList(list3);
        System.out.println(list3);
        System.out.println(list4);
        // 按照指定长度进行分割
        List<List<String>> partition = Lists.partition(list4, 2);
        System.out.println(partition);
    }

    public static void cartesianProductTest() {
        List<String> list1 = Lists.newArrayList("a", "b", "c");
        List<String> list2 = Lists.newArrayList("d", "e", "f");
        List<String> list3 = Lists.newArrayList("1", "2", "3");
        //获取多个list的笛卡尔集
        List<List<String>> list = Lists.cartesianProduct(list1, list2, list3);
        System.out.println(list);
    }

    public static void charactersOfTest() {
        //将字符串转成字符集合
        ImmutableList<Character> list = Lists.charactersOf("ababcdfb");
        for (Character character : list) {
            System.out.println(character);
        }
    }

    public static void reverseTest() {
        List<String> list = Lists.newArrayList("a", "b", "c", "1", "2", "3");
        //反转list
        List<String> reverseList = Lists.reverse(list);
        System.out.println(reverseList);

        /*List<String> reverseNew = ListUtil.reverseNew(list);
        System.out.println(reverseNew);*/
    }

    public void transFormTest() {
        List<String> list = Lists.newArrayList("a", "b", "c");
        //把list中的每个元素拼接一个1
        List<String> list1 = Lists.transform(list, str -> str + "1");
        System.out.println(list1);
        // 另一种方法
        List<String> strings = list.stream().map(bean -> bean + "1").collect(toList());


        System.out.println(strings);
    }

    public static void combinationsTest() {
        //将集合中的元素按指定的大小分隔，指定大小的所有组合
        Set<String> set1 = Sets.newHashSet("a", "b", "c", "d");
        Set<Set<String>> sets = Sets.combinations(set1, 3);
        for (Set<String> set : sets) {
            System.out.println(set);
        }
    }

    // 差集
    public static void differenceTest() {
        Set<String> set1 = Sets.newHashSet("a", "b", "d");
        Set<String> set2 = Sets.newHashSet("d", "e", "f");
        //difference返回：从set1中剔除两个set公共的元素
        System.out.println(Sets.difference(set1, set2));
    }

    // 交集
    public static void intersectionTest() {
        Set<String> set1 = Sets.newHashSet("a", "b", "c");
        Set<String> set2 = Sets.newHashSet("a", "b", "f");
        // 取两个集合的交集
        System.out.println(Sets.intersection(set1, set2));
    }

    // 并集
    public static void unionTest() {
        Set<String> set1 = Sets.newHashSet("a", "b", "c");
        Set<String> set2 = Sets.newHashSet("1", "2", "3");
        //取两个集合的并集
        System.out.println(Sets.union(set1, set2));
    }

    // set转map
    public static void asMapTest() {
        Set<String> set = Sets.newHashSet("a", "b", "c");
        //将set转成Map,key为set元素,value为每个元素的长度
        Map<String, Integer> map = Maps.asMap(set, s -> s != null ? s.length() : 0);
        System.out.println(map);

        Map<String, Integer> collect = set.stream().collect(Collectors.toMap(String::new, String::length));
        System.out.println(collect);
    }

    // 差值
    public static void differenceMapTest() {
        Map<String, String> map1 = Maps.newHashMap();
        map1.put("a", "1");
        map1.put("b", "2");
        map1.put("c", "3");
        Map<String, String> map2 = Maps.newHashMap();
        map2.put("a", "1");
        map2.put("e", "5");
        map2.put("f", "6");
        //mapDifference是将两个map相同的部分剔除
        MapDifference<String, String> mapDifference = Maps.difference(map1, map2);
        //两个Map相同的部分
        System.out.println(mapDifference.entriesInCommon());
        //左边集合剔除相同部分后的剩余
        System.out.println(mapDifference.entriesOnlyOnLeft());
        //右边集合剔除相同部分后的剩余
        System.out.println(mapDifference.entriesOnlyOnRight());
    }

    // 过滤Entry
    public void filterEntriesTest() {
        Map<String, String> map1 = Maps.newHashMap();
        map1.put("a", "1");
        map1.put("b", "2");
        map1.put("c", "3");
        Map<String, String> result = Maps.filterEntries(map1, item -> !item.getValue().equalsIgnoreCase("2"));
        System.out.println(result);
    }

    // 过滤 Key
    public void filterKeysTest() {
        Map<String, String> map1 = Maps.newHashMap();
        map1.put("a", "1");
        map1.put("b", "2");
        map1.put("c", "3");
        Map<String, String> result = Maps.filterKeys(map1, item -> !item.equalsIgnoreCase("b"));
        System.out.println(result);
    }

    // 过滤 Value
    public void filterValuesTest() {
        Map<String, String> map1 = Maps.newHashMap();
        map1.put("a", "1");
        map1.put("b", "2");
        map1.put("c", "3");
        Map<String, String> result = Maps.filterValues(map1, item -> !item.equalsIgnoreCase("3"));
        System.out.println(result);
    }

    public static void transFormEntriesTest() {
        Map<String, String> map1 = Maps.newHashMap();
        map1.put("a", "1");
        map1.put("b", "2");
        map1.put("c", "3");
        Map<String, String> result = Maps.transformEntries(map1, (k, v) -> k + v);
        System.out.println(result);
    }

    public static void transformValuesTest() {
        Map<String, String> map1 = Maps.newHashMap();
        map1.put("a", "1");
        map1.put("b", "2");
        map1.put("c", "3");
        Map<String, String> result = Maps.transformValues(map1, value -> value + 10);
        System.out.println(result);
    }


    public static void unmodifiableTest() {
        List<Integer> list = Lists.newArrayList(1, 2, 3);
        List<Integer> list1 = Collections.unmodifiableList(list);

        // [1, 2, 3]
        System.out.println(list);
        // [1, 2, 3]
        System.out.println(list1);
        // list修改，list1也会被修改
        list.add(4);
        // [1, 2, 3, 4]
        System.out.println(list1);

        List<String> fromList = Lists.newArrayList("影子", "陈萍萍", "庆帝");
        ImmutableList<String> immutableList = ImmutableList.copyOf(fromList);
        // immutableList.add("wowoow");
        System.out.println(immutableList);
    }


    public void listMultimapTest() {
        List<Integer> asList = Arrays.asList(1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4);
        ListMultimap<Integer, String>
                treeListMultimap = MultimapBuilder.treeKeys().arrayListValues().build();

        for (Integer integer : asList) {
            treeListMultimap.put(integer, RandomUtil.randomString(integer));
        }


        Map<Integer, Collection<String>> asMap = treeListMultimap.asMap();


        System.out.println(JSONUtil.toJsonPrettyStr(asMap));
    }


    public static void testCacheLoader() throws ExecutionException {
        LoadingCache<String, String> loadingCache = CacheBuilder.newBuilder()
                //最大容量为100（基于容量进行回收）
                .maximumSize(100)
                //创建一个CacheLoader，重写load方法，以实现"当get时缓存不存在，则load，放到缓存，并返回"的效果
                .build(new CacheLoader<String, String>() {
                    //重点，自动写缓存数据的方法，必须要实现
                    @Override
                    public String load(String key) {
                        return "value_" + key;
                    }

                });

        //测试例子，调用其get方法，cache会自动加载并返回
        String value = loadingCache.get("key1");
        //返回value_1
        System.out.println("value:" + value);
    }

    public void testCallableCache() throws ExecutionException {
        Cache<String, String> cache = CacheBuilder.newBuilder()
                //最大容量为100（基于容量进行回收）
                .maximumSize(100)
                .build();
        String key = "1";
        //loadingCache的定义跟上一面一样
        //get时定义一个Callable
        String value = cache.get(key, () -> "call_" + key);
        System.out.println("value:" + value);
    }


    public static void test() {
        Cache<String, String> cache = CacheBuilder.newBuilder()
                // 设置缓存容器的初始化大小为10
                .initialCapacity(10)
                // 设置缓存最大容量为20，超过100之后就会按照LRU最近虽少使用算法来移除缓存项
                .maximumSize(20)
                // 设置并发级别为8，并发级别是指可以同时写缓存的线程数
                .concurrencyLevel(8)
                // 是否需要统计缓存情况
                .recordStats()
                //读多长时间后删除
                //.expireAfterAccess(3, TimeUnit.SECONDS)
                //写多长时间后删除
                //.expireAfterWrite(3, TimeUnit.SECONDS)
                //设置缓存的移除通知
                .removalListener(notification -> System.out.println(notification.getKey() + " " + notification.getValue() + " 被移除,原因:" + notification.getCause()))
                .build();
        for (int i = 0; i < 50; i++) {
            String string =
                    RandomUtil.randomString(6);
            try {
                cache.get(string, () -> RandomUtil.randomString(4));
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        }
        displayCache(cache);


        // 清除
        // 单个
        cache.invalidate("");
        // 批量
        cache.invalidateAll(Arrays.asList());
        // 全部
        cache.invalidateAll();

        // 统计
        CacheStats stats = cache.stats();
        // 命中率
        stats.hitRate();
        // 失误率
        stats.missRate();
        // 加载次数d
        stats.loadCount();
    }

    public static void displayCache(Cache<?, ?> cache) {
        for (Object o : cache.asMap().entrySet()) {
            System.out.println(o.toString());
        }
    }


    /**
     * 随机缓存加载,实际使用时应实现业务的缓存加载逻辑,例如从数据库获取数据
     */
    public static class DemoCacheLoader extends CacheLoader<String, String> {
        @Override
        public String load(String key) throws Exception {
            System.out.println(Thread.currentThread().getName() + " 加载数据开始");
            TimeUnit.SECONDS.sleep(8);
            Random random = new Random();
            System.out.println(Thread.currentThread().getName() + " 加载数据结束");
            return "value:" + random.nextInt(10000);
        }
    }

    public Boolean getData(Long deptId) {
        return Boolean.TRUE;
    }

    private final LoadingCache<Long, Boolean> validTenantCache = buildAsyncReloadingCache(
            Duration.ofMinutes(1L), // 过期时间 1 分钟
            new CacheLoader<Long, Boolean>() {
                @Override
                public Boolean load(Long deptId) {
                    if (Objects.isNull(deptId)) {
                        return Boolean.FALSE;
                    }
                    return getData(deptId);
                }

            });

    public static <K, V> LoadingCache<K, V> buildAsyncReloadingCache(Duration duration, CacheLoader<K, V> loader) {
        return CacheBuilder.newBuilder()
                // 只阻塞当前数据加载线程，其他线程返回旧值
                .refreshAfterWrite(duration)
                // 通过 asyncReloading 实现全异步加载，包括 refreshAfterWrite 被阻塞的加载线程
                .build(CacheLoader.asyncReloading(loader, Executors.newCachedThreadPool()));
    }
}
